package com.itcam.pro01;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author : Cammy.Wu
 * Description : 
 */

public class CompletableFutureDemo05Mall {
    static List<NetMall> list = Arrays.asList(new NetMall("jd"), new NetMall("taobao"), new NetMall("dangdang"));

    /**
     * step by step
     * @param list
     * @param productName
     * @return
     */
    public static List<String> getPrice(List<NetMall> list, String productName) {
        return list.stream()
                .map(netMall -> String.format("《" + productName + "》" + "in %s price is %.2f",
                        netMall.getNetMallName(),
                        netMall.calcPrice(productName)))
                .collect(Collectors.toList());
    }

    /**
     * 该方法使用 CompletableFuture 异步地从多个网络商城获取指定商品的价格信息。
     * 它会并行地查询每个商城的价格，并在所有查询完成后返回结果列表。
     *
     * @param list 包含多个 NetMall 对象的列表，每个 NetMall 对象代表一个网络商城
     * @param productName 要查询价格的商品名称
     * @return 包含每个商城中指定商品价格信息的字符串列表
     */
    public static List<String> getPriceByCompletableFuture(List<NetMall> list, String productName) {
        // 将 NetMall 列表转换为流，以便进行后续的操作
        return list.stream()
                // 对每个 NetMall 对象创建一个 CompletableFuture，异步计算商品价格
                .map(netMall ->
                        // 使用 CompletableFuture.supplyAsync 方法异步执行任务
                        CompletableFuture.supplyAsync(() ->
                                // 格式化字符串，包含商品名称、商城名称和价格
                                String.format("《" + productName + "》" + "in %s price is %.2f",
                                        netMall.getNetMallName(),
                                        netMall.calcPrice(productName))))
                // 将流中的 CompletableFuture 收集到一个列表中
                .toList()
                // 将 CompletableFuture 列表转换为流，以便进行后续的操作
                .stream()
                // 调用 CompletableFuture 的 join 方法，等待所有异步任务完成并获取结果
                .map(CompletableFuture::join)
                // 将流中的结果收集到一个列表中
                .toList();

        /**
         * todo:    toList() 是终止操作，会把流转换为列表。而列表可以通过 stream() 方法
         *          再次转换为流，从而可以继续进行流操作。这就是为什么代码里 toList() 之后
         *          还能使用 stream() 的原因。
         */
    }


    public static void main(String[] args) {
        /**
         * 采用step by setp方式查询
         * 《masql》in jd price is 110.11
         * 《masql》in taobao price is 109.32
         * 《masql》in dangdang price is 109.24
         * ------costTime: 3094 毫秒
         */
        long StartTime = System.currentTimeMillis();
        List<String> list1 = getPrice(list, "masql");
        for (String element : list1) {
            System.out.println(element);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("------costTime: " + (endTime - StartTime) + " 毫秒");

        /**
         * 采用 all in三个异步线程方式查询
         * 《mysql》in jd price is 109.71
         * 《mysql》in taobao price is 110.69
         * 《mysql》in dangdang price is 109.28
         * ------costTime1009 毫秒
         */
        long StartTime2 = System.currentTimeMillis();
        List<String> list2 = getPriceByCompletableFuture(list, "mysql");
        for (String element : list2) {
            System.out.println(element);
        }
        long endTime2 = System.currentTimeMillis();
        System.out.println("------costTimeCompletableFuture: " + (endTime2 - StartTime2) + " 毫秒");
    }

}

@AllArgsConstructor
@NoArgsConstructor
@Data
class NetMall {
    private String netMallName;

    public double calcPrice(String productName) {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
    }
}
